package com.flan.seckill.service.impl;

import com.flan.seckill.dao.SeckillDao;
import com.flan.seckill.dao.SuccesKilledDao;
import com.flan.seckill.dto.Exposer;
import com.flan.seckill.dto.SeckillExecution;
import com.flan.seckill.entity.Seckill;
import com.flan.seckill.entity.SuccessKilled;
import com.flan.seckill.enums.SeckillStateEnum;
import com.flan.seckill.exception.RepeatKillException;
import com.flan.seckill.exception.SeckillCloseException;
import com.flan.seckill.exception.SeckillException;
import com.flan.seckill.service.SeckillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * 秒杀服务层实现类
 * Created by flan on 2017/1/3.
 */
@Service(value = "seckillServiceImpl")
public class SeckillServiceImpl implements SeckillService {

    public static final String salt = "osj%gu34958jgs983495-0)(_0429";
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private SeckillDao seckillDao;

    @Resource
    private SuccesKilledDao succesKilledDao;


    @Override
    public List<Seckill> getAllSeckillList() {
        return seckillDao.queryAll(0,4);
    }

    @Override
    public Seckill getSeckillById(long seckillId) {
        return seckillDao.queryById(seckillId);
    }

    @Override
    public Exposer exportSeckillUrl(long seckillId) {

        Seckill seckill = seckillDao.queryById(seckillId);
        if (seckill == null){
            return new Exposer(false,seckillId);
        }

        Date startTime = seckill.getStartTime();
        Date endTime = seckill.getEndTime();
        Date nowTime = new Date();

        if(nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime()){
            return new Exposer(false,seckillId,nowTime.getTime(),startTime.getTime(),endTime.getTime());
        }

        String md5 = getMD5(seckillId);
        return new Exposer(true,md5,seckillId);
    }

    private String getMD5(long seckillId){
        String base = seckillId+ "/" + salt;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;
    }

    @Override
    @Transactional
    public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException, RuntimeException, SeckillCloseException {

        if(md5 == null || !md5.equals(getMD5(seckillId))){
            throw  new SeckillException("seckill data rewrite");
        }

        //执行减库存 和 记录购买行为
        Date nowTime = new Date();
        try {
            int updateCount = seckillDao.reduceNumber(seckillId,nowTime);
            if(updateCount <= 0){
                //没有更新数据成功
                throw new SeckillCloseException("seckill closed");
            }else {
                //记录购买信息
                int insertCount = succesKilledDao.insertSuccessKilled(seckillId,userPhone);
                if(insertCount <= 0){
                    throw new RepeatKillException("seckill repeated");
                }else {
                    //秒杀成功
                    SuccessKilled successKilled = succesKilledDao.queryByIdWithSeckill(seckillId,userPhone);
                    return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS,successKilled);
                }
            }
        } catch (SeckillCloseException e1){
            throw e1;
        } catch (RuntimeException e2){
            throw e2;
        } catch (Exception e){
            logger.error(e.getMessage(),e);

            //将所有编译器异常转换成运行期异常
            throw new SeckillException("seckill inner error : "+e.getMessage());
        }
    }
}
